////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxipegamma15.cpp
/// @brief IPEGamma class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxcdmdefs.h"
#include "camxdefs.h"
#include "camxipegamma15.h"
#include "camxiqinterface.h"
#include "camxnode.h"
#include "camxtuningdatamanager.h"
#include "ipe_data.h"
#include "parametertuningtypes.h"

CAMX_NAMESPACE_BEGIN

static const UINT32 Gamma15LUTNumEntriesPerChannelSize = Gamma15LUTNumEntriesPerChannel * sizeof(UINT32);
static const UINT32 MaxGamma15LUTNumEntries            = Gamma15LUTNumEntriesPerChannel * MaxGammaLUTNum;
static const UINT32 Gamma15LUTEntrySize                = sizeof(UINT32);
static const UINT32 Gamma15LUTBufferSize               = (MaxGamma15LUTNumEntries * Gamma15LUTEntrySize);
static const UINT32 Gamma15LUTBufferDWordSize          = Gamma15LUTBufferSize / sizeof(UINT32);
static const UINT32 GammaLUTChannel0                   = 0;
static const UINT32 GammaLUTChannel1                   = 1;
static const UINT32 GammaLUTChannel2                   = 2;

static const UINT32 IFEGamma15MaxGammaValue            = 0x3FF;
static const UINT16 IFEGamma15HwPackBit                = 10;


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::Create(
    IPEModuleCreateData* pCreateData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pCreateData)
    {
        pCreateData->pModule = CAMX_NEW IPEGamma15;

        if (NULL != pCreateData->pModule)
        {
            result = pCreateData->pModule->Initialize(&pCreateData->initializationData);
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupPProc, "Module initialization failed !!");
                IPEGamma15* pModule = reinterpret_cast<IPEGamma15*>(pCreateData->pModule);
                pModule->Destroy();
                pCreateData->pModule = NULL;
            }
        }
        else
        {
            result = CamxResultENoMemory;
            CAMX_ASSERT_ALWAYS_MESSAGE("Memory allocation failed");
        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_LOG_ERROR(CamxLogGroupPProc, "Null input pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::Initialize(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    m_pLUTCmdBufferManager = CAMX_NEW CmdBufferManager();
    CAMX_ASSERT(NULL != m_pLUTCmdBufferManager);

    if (NULL != m_pLUTCmdBufferManager)
    {
        ResourceParams  cmdBufferConfig = { 0 };

        cmdBufferConfig.resourceSize                 = Gamma15LUTBufferSize;
        cmdBufferConfig.poolSize                     = (pInputData->requestQueueDepth * cmdBufferConfig.resourceSize);
        cmdBufferConfig.usageFlags.cmdBuffer         = 1;
        cmdBufferConfig.cmdParams.type               = CmdType::CDMDMI;
        cmdBufferConfig.alignment                    = CamxCommandBufferAlignmentInBytes;
        cmdBufferConfig.cmdParams.enableAddrPatching = 0;
        cmdBufferConfig.cmdParams.maxNumNestedAddrs  = 0;
        cmdBufferConfig.memFlags                     = CSLMemFlagUMDAccess;
        cmdBufferConfig.pDeviceIndices               = pInputData->pipelineIPEData.pDeviceIndex;
        cmdBufferConfig.numDevices                   = 1;

        result = m_pLUTCmdBufferManager->Initialize(&cmdBufferConfig);
    }
    else
    {
        result = CamxResultENoMemory;
        CAMX_ASSERT_ALWAYS_MESSAGE("Memory allocation failed");
    }

    if (CamxResultSuccess == result)
    {
        // Precompute Offset of Look up table with LUT command buffer for ease of patching
        m_offsetLUTCmdBuffer[GammaLUTChannel0] = 0;
        m_offsetLUTCmdBuffer[GammaLUTChannel1] = Gamma15LUTNumEntriesPerChannelSize;
        m_offsetLUTCmdBuffer[GammaLUTChannel2] = Gamma15LUTNumEntriesPerChannelSize + m_offsetLUTCmdBuffer[GammaLUTChannel1];
    }

    result = AllocateCommonLibraryData();
    if (result != CamxResultSuccess)
    {
        CAMX_LOG_ERROR(CamxLogGroupPProc, "Unable to initilize common library data, no memory");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::AllocateCommonLibraryData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::AllocateCommonLibraryData()
{
    CamxResult result = CamxResultSuccess;

    UINT interpolationSize = (sizeof(gamma_1_5_0::mod_gamma15_cct_dataType) * (Gamma15MaxNonLeafNode + 1));

    if (NULL == m_dependenceData.pInterpolationData)
    {
        // Alloc for gamma_1_5_0::mod_gamma15_cct_dataType
        m_dependenceData.pInterpolationData = CAMX_CALLOC(interpolationSize);
        if (NULL == m_dependenceData.pInterpolationData)
        {
            result = CamxResultENoMemory;
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::WriteLUTtoDMI
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::WriteLUTtoDMI(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pInputData)
    {
        CmdBuffer*  pDMICmdBuffer = pInputData->pipelineIPEData.ppIPECmdBuffer[CmdBufferDMIHeader];

        if ((NULL != pDMICmdBuffer) && (NULL != m_pLUTCmdBuffer))
        {
            // This offset holds CDM header of LTM LUTs, store this offset and node will patch it in top level payload
            m_offsetLUT = pDMICmdBuffer->GetResourceUsedDwords() * sizeof(UINT32);

            result = PacketBuilder::WriteDMI(pDMICmdBuffer,
                                             regIPE_IPE_0_PPS_CLC_GLUT_DMI_CFG,
                                             (GammaLUTChannel0 + 1),
                                             m_pLUTCmdBuffer,
                                             m_offsetLUTCmdBuffer[GammaLUTChannel0],
                                             Gamma15LUTNumEntriesPerChannelSize);
            CAMX_ASSERT(CamxResultSuccess == result);

            if (CamxResultSuccess == result)
            {
                result = PacketBuilder::WriteDMI(pDMICmdBuffer,
                                                 regIPE_IPE_0_PPS_CLC_GLUT_DMI_CFG,
                                                 (GammaLUTChannel1 + 1),
                                                 m_pLUTCmdBuffer,
                                                 m_offsetLUTCmdBuffer[GammaLUTChannel1],
                                                 Gamma15LUTNumEntriesPerChannelSize);
                CAMX_ASSERT(CamxResultSuccess == result);

                if (CamxResultSuccess == result)
                {
                    result = PacketBuilder::WriteDMI(pDMICmdBuffer,
                                                     regIPE_IPE_0_PPS_CLC_GLUT_DMI_CFG,
                                                     (GammaLUTChannel2 + 1),
                                                     m_pLUTCmdBuffer,
                                                     m_offsetLUTCmdBuffer[GammaLUTChannel2],
                                                     Gamma15LUTNumEntriesPerChannelSize);
                    CAMX_ASSERT(CamxResultSuccess == result);

                    if (CamxResultSuccess != result)
                    {
                        CAMX_ASSERT_ALWAYS_MESSAGE("Failed to write DMI data for GammaLUTChannel2");
                    }
                }
                else
                {
                    CAMX_ASSERT_ALWAYS_MESSAGE("Failed to write DMI data for GammaLUTChannel1");
                }
            }
            else
            {
                CAMX_ASSERT_ALWAYS_MESSAGE("Failed to write DMI data for GammaLUTChannel0");
            }
        }
        else
        {
            result = CamxResultEFailed;
            CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Input pointer");
        }
    }
    else
    {
        result = CamxResultEInvalidPointer;
        CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Input pointer");
    }

    return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::UpdateIPEInternalData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::UpdateIPEInternalData(
    const ISPInputData* pInputData
    ) const
{
    CamxResult     result         = CamxResultSuccess;
    IpeIQSettings* pIPEIQSettings = reinterpret_cast<IpeIQSettings*>(pInputData->pipelineIPEData.pIPEIQSettings);
    ISPHALTagsData* pHALTagsData  = pInputData->pHALTagsData;
    UINT32          toneMapCurvePoints;


    pIPEIQSettings->glutParameters.moduleCfg.EN = m_moduleEnable;

    /// @todo (CAMX-738) Add metadata information

    // Post tuning metadata if setting is enabled
    if (NULL != pInputData->pIPETuningMetadata)
    {
        CAMX_STATIC_ASSERT(Gamma15LUTNumEntriesPerChannelSize ==
                           sizeof(pInputData->pIPETuningMetadata->IPEDMIData.packedLUT.gamma[GammaLUTChannel0].curve));

        if (NULL != m_pGammaG[GammaLUTChannel0])
        {
            Utils::Memcpy(&pInputData->pIPETuningMetadata->IPEDMIData.packedLUT.gamma[GammaLUTChannel0].curve,
                          m_pGammaG[GammaLUTChannel0],
                          Gamma15LUTNumEntriesPerChannelSize);
        }

        if (NULL != m_pGammaG[GammaLUTChannel1])
        {
            Utils::Memcpy(&pInputData->pIPETuningMetadata->IPEDMIData.packedLUT.gamma[GammaLUTChannel1].curve,
                          m_pGammaG[GammaLUTChannel1],
                          Gamma15LUTNumEntriesPerChannelSize);
        }

        if (NULL != m_pGammaG[GammaLUTChannel2])
        {
            Utils::Memcpy(&pInputData->pIPETuningMetadata->IPEDMIData.packedLUT.gamma[GammaLUTChannel2].curve,
                          m_pGammaG[GammaLUTChannel2],
                          Gamma15LUTNumEntriesPerChannelSize);
        }
    }
    if (NULL != pInputData->pCalculatedData)
    {
        pInputData->pCalculatedData->toneMapData.tonemapMode = m_tonemapMode;
    }
    // Post the tone map curve
    if ((NULL != pInputData->pCalculatedData) && (TonemapModeContrastCurve == m_tonemapMode))
    {
        toneMapCurvePoints = Utils::MinUINT32(Gamma15LUTNumEntriesPerChannel,
                pInputData->pHALTagsData->tonemapCurves.curvePoints / 2);

        // tone map mode and no of tone map curve points
        pInputData->pCalculatedData->toneMapData.curvePoints = toneMapCurvePoints * 2;

        for (UINT32 idx = 0; idx < toneMapCurvePoints; idx++)
        {
            UINT32 srcIndex =
                Utils::RoundFLOAT(static_cast<FLOAT>(idx * Gamma15LUTNumEntriesPerChannel)) /(toneMapCurvePoints - 1) ;
            if (0 != srcIndex)
            {
                srcIndex -= 1;
            }

            // Green tone map curve
            pInputData->pCalculatedData->toneMapData.tonemapCurveGreen[idx].point[0] =
                static_cast<FLOAT>(idx) / (toneMapCurvePoints - 1);
            pInputData->pCalculatedData->toneMapData.tonemapCurveGreen[idx].point[1] =
                m_pGamma[GammaLUTChannelG][srcIndex] / IFEGamma15MaxGammaValue ;

            // Blue tone map curve
            pInputData->pCalculatedData->toneMapData.tonemapCurveBlue[idx].point[0] =
                static_cast<FLOAT>(idx) / (toneMapCurvePoints - 1);
            pInputData->pCalculatedData->toneMapData.tonemapCurveBlue[idx].point[1] =
                m_pGamma[GammaLUTChannelB][srcIndex] / IFEGamma15MaxGammaValue;

            // Red tone map curve
            pInputData->pCalculatedData->toneMapData.tonemapCurveRed[idx].point[0] =
                static_cast<FLOAT>(idx) / (toneMapCurvePoints - 1);
            pInputData->pCalculatedData->toneMapData.tonemapCurveRed[idx].point[1] =
                m_pGamma[GammaLUTChannelR][srcIndex] / IFEGamma15MaxGammaValue;

            CAMX_LOG_VERBOSE(CamxLogGroupISP, "idx %d, srcIndex %d, toneMapCurvePoints %d, Reporting Curve [%f, %f],"
                   "HW LUT data[%f]",
                    idx,
                    srcIndex,
                    toneMapCurvePoints,
                    pInputData->pCalculatedData->toneMapData.tonemapCurveRed[idx].point[0],
                    pInputData->pCalculatedData->toneMapData.tonemapCurveRed[idx].point[1],
                    m_pGamma[GammaLUTChannelR][srcIndex]);
        }

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "toneMapCurvePoints %d", toneMapCurvePoints);

        for ( UINT32 idx = 0; idx < toneMapCurvePoints; idx++)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupISP, "toneMapCurvePoints %d, Red Reporting Curve [idx = %d] [%f, %f]",
                toneMapCurvePoints,
                idx,
                pInputData->pCalculatedData->toneMapData.tonemapCurveRed[idx].point[0],
                pInputData->pCalculatedData->toneMapData.tonemapCurveRed[idx].point[1]);
        }

        for (UINT32 idx = 0; idx < toneMapCurvePoints; idx++)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupISP, "toneMapCurvePoints %d, Blue Reporting Curve [idx = %d] [%f, %f]",
                toneMapCurvePoints,
                idx,
                pInputData->pCalculatedData->toneMapData.tonemapCurveGreen[idx].point[0],
                pInputData->pCalculatedData->toneMapData.tonemapCurveGreen[idx].point[1]);
        }

        for (UINT32 idx = 0; idx < toneMapCurvePoints; idx++)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupISP, "toneMapCurvePoints %d, Blue Reporting Curve [idx = %d] [%f, %f]",
                toneMapCurvePoints,
                idx,
                pInputData->pCalculatedData->toneMapData.tonemapCurveBlue[idx].point[0],
                pInputData->pCalculatedData->toneMapData.tonemapCurveBlue[idx].point[1]);
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::CreateCmdList
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::CreateCmdList(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    result = WriteLUTtoDMI(pInputData);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::ValidateDependenceParams
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::ValidateDependenceParams(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    /// @todo (CAMX-730) validate dependency parameters
    if ((NULL == pInputData)                                                     ||
        (NULL == pInputData->pipelineIPEData.ppIPECmdBuffer[CmdBufferDMIHeader]) ||
        (NULL == pInputData->pipelineIPEData.pIPEIQSettings))
    {
        CAMX_LOG_ERROR(CamxLogGroupPProc,
            "Invalid Input: pInputData %p",
            pInputData);

        result = CamxResultEInvalidArg;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::DumpRegConfig
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IPEGamma15::DumpRegConfig() const
{
    /// @todo (CAMX-666) More debug logs
    CAMX_LOG_VERBOSE(CamxLogGroupPProc, "%s", __FUNCTION__);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::GetRegCmd
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* IPEGamma15::GetRegCmd()
{
    return NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::CheckDependenceChange
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL IPEGamma15::CheckDependenceChange(
    ISPInputData* pInputData)
{
    BOOL isChanged = FALSE;

    if ((NULL != pInputData)                  &&
        (NULL != pInputData->pAECUpdateData)  &&
        (NULL != pInputData->pAWBUpdateData)  &&
        (NULL != pInputData->pHwContext)      &&
        (NULL != pInputData->pTuningData)     &&
        (NULL != pInputData->pHALTagsData))
    {
        ISPHALTagsData* pHALTagsData = pInputData->pHALTagsData;

        CAMX_LOG_VERBOSE(CamxLogGroupPProc, "Lux Index [0x%f] ", pInputData->pAECUpdateData->luxIndex);

        m_tonemapMode = pHALTagsData->tonemapCurves.tonemapMode;

        // Check for manual control
        if ((TonemapModeContrastCurve == pHALTagsData->tonemapCurves.tonemapMode) &&
            (0 != pInputData->pHALTagsData->tonemapCurves.curvePoints))
        {
            m_manualCCMOverride = TRUE;
            m_moduleEnable      = TRUE;
            isChanged           = TRUE;
        }
        else
        {
            m_manualCCMOverride = FALSE;
            if (NULL != pInputData->pOEMIQSetting)
            {
                m_moduleEnable = (static_cast<OEMIPEIQSetting*>(pInputData->pOEMIQSetting))->GammaEnable;

                isChanged      = (TRUE == m_moduleEnable);
            }
            else
            {
                if (NULL != pInputData->pTuningData)
                {
                    TuningDataManager* pTuningManager = pInputData->pTuningDataManager;
                    CAMX_ASSERT(NULL != pTuningManager);

                    // Search through the tuning data (tree), only when there
                    // are changes to the tuning mode data as an optimization
                    if ((TRUE == pInputData->tuningModeChanged)    &&
                        (TRUE == pTuningManager->IsValidChromatix()))
                    {
                        m_pChromatix = pTuningManager->GetChromatix()->GetModule_gamma15_ipe(
                                           reinterpret_cast<TuningMode*>(&pInputData->pTuningData->TuningMode[0]),
                                           pInputData->pTuningData->noOfSelectionParameter);
                    }

                    m_dependenceData.pLibData = pInputData->pLibInitialData;

                    CAMX_ASSERT(NULL != m_pChromatix);
                    if (NULL != m_pChromatix)
                    {
                        if ((m_pChromatix != m_dependenceData.pChromatix) ||
                            (m_pChromatix->SymbolTableID != m_dependenceData.pChromatix->SymbolTableID) ||
                            (m_pChromatix->enable_section.gamma_enable != m_moduleEnable))
                        {
                            m_dependenceData.pChromatix = m_pChromatix;
                            m_moduleEnable              = m_pChromatix->enable_section.gamma_enable;

                            isChanged = (TRUE == m_moduleEnable);
                        }

                        if (TRUE == m_moduleEnable)
                        {
                            if (TRUE == IQInterface::s_interpolationTable.gamma15TriggerUpdate(
                                            &pInputData->triggerData, &m_dependenceData))
                            {
                                isChanged = TRUE;
                            }

                            if (m_dependenceData.contrastLevel != pHALTagsData->contrastLevel)
                            {
                                isChanged = TRUE;
                                m_dependenceData.contrastLevel = pHALTagsData->contrastLevel;
                            }
                        }
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupPProc, "Failed to get Chromatix");
                    }
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupPProc, "Tuning Pointer is NULL");
                }
            }
        }
    }
    else
    {
        if (NULL != pInputData)
        {
            CAMX_LOG_ERROR(CamxLogGroupPProc,
                           "Invalid Input: pNewAECUpdate %p pNewAWBUpdate %p pHwContext %p",
                           pInputData->pAECUpdateData,
                           pInputData->pAWBUpdateData,
                           pInputData->pHwContext);
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupPProc, "Null Input Pointer");
        }
    }

    return isChanged;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::FetchDMIBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::FetchDMIBuffer()
{
    CamxResult      result          = CamxResultSuccess;
    PacketResource* pPacketResource = NULL;

    CAMX_ASSERT(NULL != m_pLUTCmdBufferManager);

    // Recycle the last updated LUT DMI cmd buffer
    if (NULL != m_pLUTCmdBuffer)
    {
        m_pLUTCmdBufferManager->Recycle(m_pLUTCmdBuffer);
    }

    // fetch a fresh LUT DMI cmd buffer
    result = m_pLUTCmdBufferManager->GetBuffer(&pPacketResource);
    if (CamxResultSuccess == result)
    {
        m_pLUTCmdBuffer = static_cast<CmdBuffer*>(pPacketResource);
    }
    else
    {
        m_pLUTCmdBuffer = NULL;
        result          = CamxResultEUnableToLoad;
        CAMX_ASSERT_ALWAYS_MESSAGE("Failed to fetch DMI Buffer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::InterpretGammaCurve
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void IPEGamma15::InterpretGammaCurve(
    const ISPInputData*  pInputData,
    ISPTonemapPoint*     pManualCurve,
    FLOAT*               pDerivedCurve)
{
    UINT32 dstIndex;
    UINT32 index1;
    UINT32 index2;
    FLOAT referenceIndex;
    FLOAT result;

    UINT32 appCurvePoints = pInputData->pHALTagsData->tonemapCurves.curvePoints / 2;

    // Application sends the gamma values from the range of 0.0 to 1.0 , and also the number of curve points
    // can be <= to the max gamma cure points. We need to interpret it into the ISP native format and count

    // We are assuming referenceIndex coordinate of input curve is evenly sampled.
    for (dstIndex = 0; dstIndex < Gamma15LUTNumEntriesPerChannel; dstIndex++)
    {
        referenceIndex = 1.0f * dstIndex * (appCurvePoints - 1) / (Gamma15LUTNumEntriesPerChannel - 1);

        index1 = static_cast<UINT32>(referenceIndex);
        index2 = index1 + 1;

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "GDBG: referenceIndex %f, dstIndex %u, appCurvePoints %u,"
            "Gamma15LUTNumEntriesPerChannel %u, index1 %u, index2 %u",
            referenceIndex, dstIndex, appCurvePoints, appCurvePoints, Gamma15LUTNumEntriesPerChannel, index1, index2);

        if (index1 < (appCurvePoints - 1))
        {
            result = IFEGamma15MaxGammaValue *
                (((referenceIndex - index1) * pManualCurve[index2].point[1]) +
                 ((index2 - referenceIndex) * pManualCurve[index1].point[1]));
        }
        else
        {
            result = IFEGamma15MaxGammaValue * pManualCurve[index1].point[1];
        }

        if (result > IFEGamma15MaxGammaValue)
        {
            pDerivedCurve[dstIndex] = IFEGamma15MaxGammaValue;
        }
        else
        {
            pDerivedCurve[dstIndex] = result;
        }

        CAMX_LOG_VERBOSE(CamxLogGroupISP,
                         "idx1 = %d, idxx2 = %d, refId = %f, point[index1] = %f,point[index2] = %f, pDerivedCurve[%d] = %f",
                         index1,
                         index2,
                         referenceIndex,
                         pManualCurve[index1].point[1],
                         pManualCurve[index2].point[1],
                         dstIndex,
                         pDerivedCurve[dstIndex]);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::PackDMIData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void IPEGamma15::PackDMIData(
    FLOAT*  pGammaTable,
    UINT32* pLUT)
{
    INT32  LUTDelta = 0;
    UINT16 x        = 0;
    UINT16 y        = 0;
    UINT32 i        = 0;
    UINT32  tmp;

    const UINT16 mask = NMAX10 - 1;

    // Gamma table in Chromatix has 256 entries and the HW LUT has 255. The 255th chromatix entry is used to
    // compute the delta of last HW LUT entry.
    for (i = 0; i < Gamma15LUTNumEntriesPerChannel; i++)
    {

        tmp = static_cast<UINT32>(pGammaTable[i]);
        x   = static_cast<UINT16>(IQSettingUtils::ClampUINT32(tmp, 0, (NMAX10 - 1)));

        if (i < Gamma15LUTNumEntriesPerChannel - 1)
        {
            tmp = static_cast<UINT32>(pGammaTable[i + 1]);
            y = static_cast<UINT16>(IQSettingUtils::ClampUINT32(tmp, 0, (NMAX10 - 1)));
        }
        else
        {
            y =  NMAX10;
        }

        LUTDelta = y - x;
        LUTDelta = Utils::ClampINT32(LUTDelta, MIN_INT10, MAX_INT10);
        LUTDelta = (LUTDelta & mask);  // bpp
        // Each Gamma LUT entry has 10 bit LSB base value and 10 bit MSB delta
        pLUT[i]   = (x) | (LUTDelta << IFEGamma15HwPackBit);
    }


    return;
}




////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::UpdateManualToneMap
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void IPEGamma15::UpdateManualToneMap(
    const ISPInputData* pInputData)
{
    UINT32* pGammaDMIAddr = reinterpret_cast<UINT32*>(m_pLUTCmdBuffer->BeginCommands(Gamma15LUTBufferDWordSize));
    ISPHALTagsData* pHALTagsData = pInputData->pHALTagsData;

    // Interpret the gamma curve from app entries from 0 - 1
    InterpretGammaCurve(pInputData, &pHALTagsData->tonemapCurves.tonemapCurveBlue[0], &m_manualGammaCurve.gammaB[0]);
    InterpretGammaCurve(pInputData, &pHALTagsData->tonemapCurves.tonemapCurveGreen[0], &m_manualGammaCurve.gammaG[0]);
    InterpretGammaCurve(pInputData, &pHALTagsData->tonemapCurves.tonemapCurveRed[0], &m_manualGammaCurve.gammaR[0]);

    for (UINT32 i = 0; i < Gamma15LUTNumEntriesPerChannel; i += 8)
    {

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "Red Curve i [%d to %d][%f, %f, %f, %f, %f, %f, %f, %f]",
            i,
            (i + 8),
            m_manualGammaCurve.gammaR[i + 0],
            m_manualGammaCurve.gammaR[i + 1],
            m_manualGammaCurve.gammaR[i + 2],
            m_manualGammaCurve.gammaR[i + 3],
            m_manualGammaCurve.gammaR[i + 4],
            m_manualGammaCurve.gammaR[i + 5],
            m_manualGammaCurve.gammaR[i + 6],
            m_manualGammaCurve.gammaR[i + 7]);
    }

    for (UINT32 i = 0; i < Gamma15LUTNumEntriesPerChannel; i += 8)
    {

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "Green Curve i [%d to %d][%f, %f, %f, %f, %f, %f, %f, %f]",
            i,
            (i + 8),
            m_manualGammaCurve.gammaG[i + 0],
            m_manualGammaCurve.gammaG[i + 1],
            m_manualGammaCurve.gammaG[i + 2],
            m_manualGammaCurve.gammaG[i + 3],
            m_manualGammaCurve.gammaG[i + 4],
            m_manualGammaCurve.gammaG[i + 5],
            m_manualGammaCurve.gammaG[i + 6],
            m_manualGammaCurve.gammaG[i + 7]);
    }

    for (UINT32 i = 0; i < Gamma15LUTNumEntriesPerChannel; i += 8)
    {

        CAMX_LOG_VERBOSE(CamxLogGroupISP, "Blue Curve i [%d to %d][%f, %f, %f, %f, %f, %f, %f, %f]",
            i,
            (i + 8),
            m_manualGammaCurve.gammaB[i + 0],
            m_manualGammaCurve.gammaB[i + 1],
            m_manualGammaCurve.gammaB[i + 2],
            m_manualGammaCurve.gammaB[i + 3],
            m_manualGammaCurve.gammaB[i + 4],
            m_manualGammaCurve.gammaB[i + 5],
            m_manualGammaCurve.gammaB[i + 6],
            m_manualGammaCurve.gammaB[i + 7]);
    }


    // pGammaDMIAddr = reinterpret_cast<UINT32*>(pGammaDMIAddr);
    if (NULL != pGammaDMIAddr)
    {
        pGammaDMIAddr = reinterpret_cast<UINT32*>(reinterpret_cast<UCHAR*>(pGammaDMIAddr));
        PackDMIData(&m_manualGammaCurve.gammaG[0], pGammaDMIAddr);
        m_pGamma[GammaLUTChannelG] = &m_manualGammaCurve.gammaG[0];

        pGammaDMIAddr = reinterpret_cast<UINT32*>(reinterpret_cast<UCHAR*>(pGammaDMIAddr) + Gamma15LUTNumEntriesPerChannelSize);
        PackDMIData(&m_manualGammaCurve.gammaB[0], pGammaDMIAddr);
        m_pGamma[GammaLUTChannelB] = &m_manualGammaCurve.gammaB[0];

        // pGammaDMIAddr = reinterpret_cast<UINT32*>(reinterpret_cast<UCHAR*>(pGammaDMIAddr) + IFEGamma16LutTableSize);
        pGammaDMIAddr = reinterpret_cast<UINT32*>(reinterpret_cast<UCHAR*>(pGammaDMIAddr) + Gamma15LUTNumEntriesPerChannelSize);
        PackDMIData(&m_manualGammaCurve.gammaR[0], pGammaDMIAddr);
        m_pGamma[GammaLUTChannelR] = &m_manualGammaCurve.gammaR[0];
    }

    // Commit the data
    m_pLUTCmdBuffer->CommitCommands();

}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::RunCalculation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::RunCalculation(
    const ISPInputData* pInputData)
{
    CamxResult result                            = CamxResultSuccess;
    const IPEInstanceProperty* pInstanceProperty = &pInputData->pipelineIPEData.instanceProperty;

    CAMX_LOG_VERBOSE(CamxLogGroupPProc, "Lux Index [0x%f] ", pInputData->pAECUpdateData->luxIndex);

    result = FetchDMIBuffer();

    if (CamxResultSuccess == result)
    {
        CAMX_LOG_INFO(CamxLogGroupISP, "ISP_DBG: m_manualCCMOverride %d", m_manualCCMOverride);

        if ((pInputData->parentNodeID == IFE) &&
            (pInstanceProperty->profileId == IPEProfileId::IPEProfileIdHDR10))
        {
            m_dependenceData.isHDR10Mode = TRUE;
            if (pInstanceProperty->processingType == IPEProcessingType::IPEProcessingPreview)
            {
                m_dependenceData.usecaseMode = CONFIG_PREVIEW;
            }
            else
            {
                m_dependenceData.usecaseMode = CONFIG_VIDEO;
            }
        }

        if (TRUE == m_manualCCMOverride)
        {
            UpdateManualToneMap(pInputData);
        }
        else
        {
            Gamma15OutputData outputData;
            UINT32* pLUT = reinterpret_cast<UINT32*>(m_pLUTCmdBuffer->BeginCommands(Gamma15LUTBufferDWordSize));

            outputData.type = PipelineType::IPE;
            for (UINT count = 0; count < MaxGammaLUTNum; count++)
            {
                outputData.pLUT[count] =
                    reinterpret_cast<UINT32*>(reinterpret_cast<UCHAR*>(pLUT) + m_offsetLUTCmdBuffer[count]);
            }

            result = IQInterface::IPEGamma15CalculateSetting(&m_dependenceData, pInputData->pOEMIQSetting, &outputData);
            if (CamxResultSuccess == result)
            {
                result = m_pLUTCmdBuffer->CommitCommands();
            }

            if (CamxResultSuccess == result)
            {
                if (NULL != pInputData->pIPETuningMetadata)
                {
                    m_pGammaG[GammaLUTChannel0] = outputData.pLUT[GammaLUTChannel0];
                    m_pGammaG[GammaLUTChannel1] = outputData.pLUT[GammaLUTChannel1];
                    m_pGammaG[GammaLUTChannel2] = outputData.pLUT[GammaLUTChannel2];
                }
            }
            else
            {
                m_pGammaG[GammaLUTChannel0] = NULL;
                m_pGammaG[GammaLUTChannel1] = NULL;
                m_pGammaG[GammaLUTChannel2] = NULL;
                CAMX_LOG_ERROR(CamxLogGroupPProc, "Gamma15 Calculation Failed. result %d", result);
            }
        }

#if DEBUG
        DumpRegConfig();
#endif // DEBUG
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupPProc, "Cannot get buffer from CmdBufferManager");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::Execute
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult IPEGamma15::Execute(
    ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pInputData)
    {
        // Check if dependency is published and valid
        result = ValidateDependenceParams(pInputData);

        if (CamxResultSuccess == result)
        {
            if (TRUE == CheckDependenceChange(pInputData))
            {
                result = RunCalculation(pInputData);
            }

            // Regardless of any update in dependency parameters, command buffers and IQSettings/Metadata shall be updated.
            if ((CamxResultSuccess == result) && (TRUE == m_moduleEnable))
            {
                result = CreateCmdList(pInputData);
            }

            if (CamxResultSuccess == result)
            {
                result = UpdateIPEInternalData(pInputData);
            }

            if (CamxResultSuccess != result)
            {
                CAMX_ASSERT_ALWAYS_MESSAGE("Operation failed %d", result);
            }
        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_ASSERT_ALWAYS_MESSAGE("Null Input Pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::DeallocateCommonLibraryData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IPEGamma15::DeallocateCommonLibraryData()
{
    if (NULL != m_dependenceData.pInterpolationData)
    {
        CAMX_FREE(m_dependenceData.pInterpolationData);
        m_dependenceData.pInterpolationData = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPEGamma15::IPEGamma15
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
IPEGamma15::IPEGamma15()
{
    m_type          = ISPIQModuleType::IPEGamma;
    m_moduleEnable  = TRUE;
    m_cmdLength     = 0;
    m_numLUT        = MaxGammaLUTNum;
    m_pLUTCmdBuffer = NULL;
    m_pChromatix    = NULL;

    m_pGamma[GammaLUTChannelG] = NULL;
    m_pGamma[GammaLUTChannelB] = NULL;
    m_pGamma[GammaLUTChannelR] = NULL;

    CAMX_LOG_VERBOSE(CamxLogGroupPProc, "IPE Gamma m_cmdLength %d ", m_cmdLength);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IPEGamma15::~IPEGamma15
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
IPEGamma15::~IPEGamma15()
{
    DeallocateCommonLibraryData();
    if (NULL != m_pLUTCmdBufferManager)
    {
        if (NULL != m_pLUTCmdBuffer)
        {
            m_pLUTCmdBufferManager->Recycle(m_pLUTCmdBuffer);
            m_pLUTCmdBuffer = NULL;
        }

        m_pLUTCmdBufferManager->Uninitialize();
        CAMX_DELETE m_pLUTCmdBufferManager;
        m_pLUTCmdBufferManager = NULL;
    }

    m_pChromatix = NULL;
}

CAMX_NAMESPACE_END
